﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using HippoMapper.Entity.Base;
using HippoEntity;
using System.Resources;
using System.CodeDom.Compiler;

namespace HippoMapper.MapperService
{
    class EntityProxyGenerator
    {

        private static readonly string Proxy_Class_Name = "@Proxy@";

        private static readonly string Delay_Class_Name = "@DelayClass@";

        private static readonly string Property_Type_Name = "@Type@";

        private static readonly string Property_Name = "@Property_Name@";

        private static readonly string Sync_Name = "@Sync_Name@";

        private static readonly string CodeContent = "@Content@";

        private static readonly string Init_Property = "@Init_Property@";

        internal static Type GetDelayEntityProxyType(Type delayEntityType)
        {
            if(delayEntityType.GetInterface((typeof(IDelayEntity).FullName)) == null)
                return null;

            Assembly assembly = Assembly.GetExecutingAssembly();

            ResourceManager resourceManager = new ResourceManager("HippoMapper.MapperService.ProxyEntityTemplate", assembly);

            resourceManager.IgnoreCase = true;

            string codeTemplate = resourceManager.GetString("CodeTemplate");

            string overridePropertyTemplate = resourceManager.GetString("OverridePropertyTemplate");

            string normalPropertyTemplate = resourceManager.GetString("NormalPropertyTemplate");

            string delayClassName = delayEntityType.FullName;

            string proxyClassName = delayEntityType.Name + "_Proxy";

            StringBuilder overridePropertyStrs = new StringBuilder();

            StringBuilder propertyInitStrs = new StringBuilder();

            foreach (PropertyInfo pi in delayEntityType.GetProperties(BindingFlags.Instance
                        | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.InvokeMethod
                        | BindingFlags.Static | BindingFlags.DeclaredOnly))
            {
                object[] attributes = pi.GetCustomAttributes(typeof(DelayPropertyAttribute), false);

                //Non-Delay_Load Property

                if (attributes == null || attributes.Length == 0)
                {
                    if (pi.CanWrite)
                    {
                        string propertyName = pi.Name;

                        propertyInitStrs.Append("this.").Append(propertyName);

                        propertyInitStrs.Append(" = _instance.").Append(propertyName).Append(";\r\n\r\n");
                    }
                }
                else
                {
                    foreach (Attribute attribute in attributes)
                    {
                        string function = overridePropertyTemplate.Clone() as string;

                        function = function.Replace(Sync_Name, ((DelayPropertyAttribute)attribute).PropertyName);

                        function = function.Replace(Property_Type_Name, pi.PropertyType.FullName);

                        function = function.Replace(Property_Name, pi.Name);

                        overridePropertyStrs.Append(function).Append("\r\n\r\n");
                    }
                }
            }

            codeTemplate = codeTemplate.Replace(Delay_Class_Name, delayClassName);

            codeTemplate = codeTemplate.Replace(Proxy_Class_Name, proxyClassName);

            codeTemplate = codeTemplate.Replace(Init_Property, propertyInitStrs.ToString());

            codeTemplate = codeTemplate.Replace(CodeContent, overridePropertyStrs.ToString());

            Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();

            CompilerParameters parameter = new System.CodeDom.Compiler.CompilerParameters();

            parameter.OutputAssembly = Assembly.GetExecutingAssembly().FullName;

            parameter.ReferencedAssemblies.Add("HippoEntity.dll");

            parameter.ReferencedAssemblies.Add("HippoMapper.dll");

            parameter.ReferencedAssemblies.Add(delayEntityType.Assembly.Location);

            parameter.GenerateInMemory = true;

            parameter.GenerateExecutable = false;

            parameter.TreatWarningsAsErrors = false;

            CompilerResults result = codeProvider.CompileAssemblyFromSource(parameter, new string[] { codeTemplate });

            if (result.Errors.Count > 0)
            {
                foreach (CompilerError error in result.Errors)
                    Console.WriteLine(error.ToString());
            }

            return result.CompiledAssembly.GetType("HippoMapper." + proxyClassName);
        }
    }
}
